package satir.framework;

import java.util.Hashtable;

import org.json.*;

import satir.SatirGame;
import satir.card.Card;
import satir.card.Deck;
import satir.card.factory.CardFactory;
import satir.cell.Cell;
import satir.cell.factory.CellFactory;
import satir.command.Command;
import satir.command.factory.CommandFactory;

/**
 * The Class LazyInitiator.
 * 
 * @version 1.0 February 2012
 * @author Chris Chan
 */
public class LazyInitiator {

	/** The command pool. */
	private static Hashtable<String, Command> commandPool = new Hashtable<String, Command>();
	
	/** The command fact pool. */
	private static Hashtable<String, CommandFactory> commandFactPool = new Hashtable<String, CommandFactory>();
	
	/** The cell pool. */
	private static Hashtable<String, Cell> cellPool = new Hashtable<String, Cell>();
	
	/** The cell fact pool. */
	private static Hashtable<String, CellFactory> cellFactPool = new Hashtable<String, CellFactory>();
	
	/** The card pool. */
	private static Hashtable<String, Card> cardPool = new Hashtable<String, Card>();
	
	/** The card fact pool. */
	private static Hashtable<String, CardFactory> cardFactPool = new Hashtable<String, CardFactory>();
	
	/** The deck pool. */
	private static Hashtable<String, Deck> deckPool = new Hashtable<String, Deck>();

	/**
	 * Gets the command.
	 *
	 * @param key the key of command
	 * @param isNewInstance to determine create a new instance or not
	 * @return the command
	 */
	public static Command getCommand(String key, boolean isNewInstance) {
		Command result = null;
		if (commandPool.containsKey(key) && !isNewInstance) {
			result = commandPool.get(key);
		} else {
			try {
				JSONIOPerformer satirGameConfig = new JSONIOPerformer("SatirGame.config");
				JSONObject cmdJson = satirGameConfig.retrieve().getJSONObject("implementation").getJSONObject("Command");
				JSONArray cmdClassArr = cmdJson.getJSONArray("implementation");
				JSONObject cmdClass = getJSONObjectFromArrayByKey(cmdClassArr, key);
				String factKey = cmdClass.getString("class") + "Factory";

				CommandFactory cmdFactory;
				if (commandFactPool.containsKey(factKey)) {
					cmdFactory = commandFactPool.get(factKey);
				} else {
					String factoryClassName = cmdJson.getString("package") + ".factory." + factKey;
					cmdFactory = (CommandFactory) Class.forName(factoryClassName).newInstance();
				}
				cmdFactory.setSatirGame(SatirGame.constant);
				result = cmdFactory.createCommand();
				commandPool.put(key, result);
			} catch (Exception e) {
				e.printStackTrace();
				System.exit(0);
			}
		}
		return result;
	}

	/**
	 * Gets the cell.
	 *
	 * @param key the key
	 * @param isNewInstance the is new instance
	 * @return the cell
	 */
	public static Cell getCell(String key, boolean isNewInstance) {
		Cell result = null;
		if (cellPool.containsKey(key) && !isNewInstance) {
			result = cellPool.get(key);
		} else {
			try {
				JSONIOPerformer satirGameConfig = new JSONIOPerformer("SatirGame.config");
				JSONObject cellJson = satirGameConfig.retrieve().getJSONObject("implementation").getJSONObject("Cell");
				JSONArray cellClassArr = cellJson.getJSONArray("implementation");
				JSONObject cellClass = getJSONObjectFromArrayByKey(cellClassArr, key);

				JSONIOPerformer cellConfig = new JSONIOPerformer("cell.config");
				JSONObject cellConfigJson = cellConfig.retrieve();

				String factKey = cellClass.getString("class") + "Factory";
				CellFactory cellFactory;
				if (cellFactPool.containsKey(factKey)) {
					cellFactory = cellFactPool.get(factKey);
				} else {
					String factoryClassName = cellJson.getString("package") + ".factory." + factKey;
					cellFactory = (CellFactory) Class.forName(factoryClassName).newInstance();
				}
				cellFactory.setCellDetail(cellConfigJson);
				result = cellFactory.createCell();
				cellPool.put(key, result);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return result;
	}

	/**
	 * Gets the card.
	 *
	 * @param cardType the card type
	 * @param key the key
	 * @return the card
	 */
	public static Card getCard(String cardType, String key) {
		Card result = null;
		if (cardPool.containsKey(key)) {
			result = cardPool.get(key);
		} else {
			JSONIOPerformer satirGameConfig;
			try {
				satirGameConfig = new JSONIOPerformer("SatirGame.config");
				JSONObject cardJson = satirGameConfig.retrieve().getJSONObject("implementation").getJSONObject("Card");
				JSONArray cardClassArr = cardJson.getJSONArray("implementation");
				JSONObject cardClass = getJSONObjectFromArrayByKey(cardClassArr, cardType);

				JSONIOPerformer cardConfig = new JSONIOPerformer(cardClass.getString("configFile"));
				JSONArray cardsDataArr = cardConfig.retrieve().getJSONArray(cardType);
				JSONObject cardsData = getJSONObjectFromArrayByKey(cardsDataArr, key);
				JSONObject dataForFactory = cardsData;
				if (cardsData.has("questions")) {
					JSONArray cardquestionArr = cardsData.getJSONArray("questions");

					JSONArray questionJsons = new JSONArray();
					JSONIOPerformer questionIO = new JSONIOPerformer("Question.config");
					for (int i = 0; i < cardquestionArr.length(); i++) {
						JSONArray questionsArr = questionIO.retrieve().getJSONArray("Questions");
						JSONObject questionJSON = getJSONObjectFromArrayByKey(questionsArr, cardquestionArr.getString(i));
						questionJsons.put(questionJSON);
					}
					dataForFactory = new JSONObject();
					dataForFactory.put("name", cardsData.get("name"));
					dataForFactory.put("questions", questionJsons);
				}
				String factKey = cardType + "Factory";
				CardFactory cardFactory;
				if (cardFactPool.containsKey(factKey)) {
					cardFactory = cardFactPool.get(factKey);
				} else {
					String factoryClassName = cardJson.getString("package") + ".factory." + factKey;
					cardFactory = (CardFactory) Class.forName(factoryClassName).newInstance();
				}
				cardFactory.setCardDetail(dataForFactory);
				result = cardFactory.createCard();
				cardPool.put(key, result);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return result;
	}

	/**
	 * Gets the deck.
	 *
	 * @param cardType the card type
	 * @return the deck
	 */
	public static Deck getDeck(String cardType) {
		Deck result = null;
		if (deckPool.containsKey(cardType)) {
			result = deckPool.get(cardType);
		} else {
			JSONIOPerformer deckConfig;
			try {
				result = new Deck();
				deckConfig = new JSONIOPerformer("Deck.config");
				JSONArray deckJSONArray = deckConfig.retrieve().getJSONArray("Deck");
				JSONObject cardJson = getJSONObjectFromArrayByKey(deckJSONArray, cardType);
				if (cardType.equals("MagicToolCard")) {
					JSONArray atCardKeysJSArr = cardJson.getJSONArray("atcardKeys");
					String cardKeys[] = new String[atCardKeysJSArr.length()];
					for (int i = 0; i < atCardKeysJSArr.length(); i++) {
						cardKeys[i] = atCardKeysJSArr.getString(i);
					}
					result.loadCard("AccomplishToolCard", cardKeys);
					JSONArray skipCardKeysJSArr = cardJson.getJSONArray("skipcardKeys");
					cardKeys = new String[skipCardKeysJSArr.length()];
					for (int i = 0; i < skipCardKeysJSArr.length(); i++) {
						cardKeys[i] = skipCardKeysJSArr.getString(i);
					}
					result.loadCard("SkipToolCard", cardKeys);
				} else {
					JSONArray cardKeysJSArr = cardJson.getJSONArray("cardKeys");
					String cardKeys[] = new String[cardKeysJSArr.length()];
					for (int i = 0; i < cardKeysJSArr.length(); i++) {
						cardKeys[i] = cardKeysJSArr.getString(i);
					}
					result.loadCard(cardType, cardKeys);
				}
				deckPool.put(cardType, result);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return result;
	}

	/**
	 * Gets the jSON object from array by key.
	 *
	 * @param jsArray the js array
	 * @param key the key
	 * @return the jSON object from array by key
	 * @throws JSONException the jSON exception
	 */
	private static JSONObject getJSONObjectFromArrayByKey(JSONArray jsArray, String key) throws JSONException {
		JSONObject result = null;
		for (int i = 0; i < jsArray.length() && (result == null); i++) {
			if (jsArray.getJSONObject(i).getString("key").equals(key)) {
				result = jsArray.getJSONObject(i);
			}
		}
		return result;
	}
}
